#include <math.h>
#include <stdlib.h>
#include <string>
#include "Point.h"
#include "ImageBuffer.h"
#include "Buffer.h"
#include "Definitions.h"
#include "Config.h"

/*********************************************/
std::bitset<4> Point3D::getClipCode() const
{
	std::bitset<4>		code;
	Config 			*cfg = Config::Instance();
	uint32			width = cfg->get("INTERNAL_SCREEN_WIDTH");
	uint32 			height = cfg->get("INTERNAL_SCREEN_HEIGHT");

	if ( x < 0 ) code.set(3);
	if ( x > width ) code.set(2);
	if ( y < 0 ) code.set(1);
	if ( y > height ) code.set(0);

	return (code);
}
/*********************************************/
void Point3D::draw(const uint16 r) const
{
	ImageBuffer<Color>* frame = BufferControl::Instance()->getFrameBuffer();
	fl32		Rx;
#if defined CLIP2D_COHENSUTHERLAND
	Config* 	cfg = Config::Instance();
	fl32		Lx;
	sint16		lowest = y - r;
	sint16		highest = y + r;
	sint16		rightmost = x + r;
	sint16		leftmost = x - r;
	Point3D		top(x, y + r, 0.0f);
	Point3D		bottom(x, y - r, 0.0f);
	Point3D		left(x - r, y, 0.0f);
	Point3D		right(x + r, y, 0.0f);
	uint32		width = cfg->get("INTERNAL_SCREEN_WIDTH");
	uint32 		height = cfg->get("INTERNAL_SCREEN_HEIGHT");
	std::bitset<4>	topCode = top.getClipCode();
	std::bitset<4>	bottomCode = bottom.getClipCode();
	std::bitset<4>	leftCode = left.getClipCode();
	std::bitset<4>	rightCode = right.getClipCode();
	std::bitset<4>	visible(std::string("0000"));

	if (( topCode != visible ) && ( bottomCode != visible ) && ( leftCode != visible ) && ( rightCode != visible )) return;
	if ( topCode.test(0) ) highest = height;
	if ( bottomCode.test(1) ) lowest = 0;
	if ( rightCode.test(2) ) rightmost = width;
	if ( leftCode.test(3) )	leftmost = 0;

	highest = highest - y;
	lowest = lowest -y;
	rightmost = rightmost -x;
	leftmost = leftmost - x;
#else
	uint16		lowest = -r;
	uint16		highest = r;
	uint16		rightmost = r;
	uint16		leftmost = -r;
#endif

	for ( sint16 scanline = lowest; scanline<highest; scanline++ ) {
#if defined CLIP2D_COHENSUTHERLAND
		Rx = sqrt((r*r) - (scanline*scanline));
		Lx = -Rx;
		if ( Rx > rightmost ) Rx = rightmost;
		if ( Lx < leftmost ) Lx = leftmost;
		for ( sint16 x = Lx; x<Rx; x++ ) {
			frame->setPixel( x + Point3D::x, scanline + Point3D::y, c);
		}
#else	
		Rx = sqrt((r*r) - (scanline*scanline));
		for ( sint16 x = -Rx; x<Rx; x++ ) {
			frame->setPixel( x + Point3D::x, scanline + Point3D::y, c);
		}
#endif
	}
}
/*********************************************/
void Point3D::draw() const
{
	ImageBuffer<Color>* frame = BufferControl::Instance()->getFrameBuffer();

#if defined CLIP2D_COHENSUTHERLAND
	if ( getClipCode() != std::bitset<4>(std::string("0000")) ) return;
#endif

	frame->setPixel( x, y, c );
}
/*********************************************/
void Point3D::setColor(const Color& c )
{
	Point3D::c = c;
}
/*********************************************/
Color Point3D::getColor() const
{
	return( c );
}
/*********************************************/
void Point3D::getCoordinates( fl32& x, fl32& y, fl32& z ) const
{
	x = Point3D::x;
	y = Point3D::y;
	z = Point3D::z;
}
/*********************************************/
void Point3D::setCoordinates( const fl32& x, const fl32& y, const fl32& z )
{
	Point3D::x = x;
	Point3D::y = y;
	Point3D::z = z;
}
/*********************************************/
bool Point3D::operator==(const Point3D& p) const
{
	if (( x == p.x ) && ( y == p.y ) && ( z == p.z ) && ( c == p.c )) return true;
	else return false;
}
/*********************************************/
void Point3D::operator=(const Point3D& p)
{
	x = p.x;
	y = p.y;
	z = p.z;	
	c = p.c;
}
/*********************************************/
Point3D::Point3D( fl32 x, fl32 y, fl32 z )
{
	Point3D::x = x;
	Point3D::y = y;
	Point3D::z = z;
	c = Color( 0,0,0 );
}
/*********************************************/
Point3D::Point3D( fl32 x, fl32 y, fl32 z, const Color& c )
{
	Point3D::x = x;
	Point3D::y = y;
	Point3D::z = z;
	Point3D::c = c;
}
/*********************************************/
Point3D::Point3D()
{
	x = 0.0f;
	y = 0.0f;
	z = 0.0f;
	c = Color(0, 0, 0);
}
/*********************************************/
Point3D::Point3D( const Point3D& p )
{
	x = p.x;
	y = p.y;
	z = p.z;
	c = p.c;
}
/*********************************************/
std::ostream& operator<<(std::ostream& ostr, const Point3D& p)
{
	fl32	x,y,z;
	Color	c = p.getColor();
	p.getCoordinates(x,y,z);
	ostr << "Point3D: " << x << " | " << y << " | " << z << " @ Color: " << c;
}